/*******************************************************************************
 * Includes
 ******************************************************************************/
#include "Wed_framework_config.h"
#include "Wed_uart.h"

/*******************************************************************************
 * Static
 ******************************************************************************/
static struct Wed_list_head __g_uart_list;
static struct Wed_mutex     __g_uart_list_lock;

/*******************************************************************************
 * Code
 ******************************************************************************/
/**
 * \brief UART 设备链表初始化
 *
 * @return 成功返回 0
 */
int Wed_uart_list_init(void){
    Wed_list_head_init(&__g_uart_list);

    return Wed_mutex_init(&__g_uart_list_lock);
}

/**
 * \brief 设备添加函数
 */
static int __dev_add(struct Wed_uart *p_uart_dev){
    int ret = 0;

    ret = Wed_mutex_lock(&__g_uart_list_lock, 5000);
    if (ret != 0) {
        return ret;
    }

    Wed_list_node_add_tail(&p_uart_dev->node, &__g_uart_list);

    Wed_mutex_unlock(&__g_uart_list_lock);

    return ret;
}

/**
 * \brief UART 设备写函数
 */
static int __dev_init(void *p_dev){
    struct Wed_uart *p_uart_dev = (struct Wed_uart *)p_dev;

    if (p_uart_dev->p_opts->p_fn_init) {
        return p_uart_dev->p_opts->p_fn_init(p_uart_dev->p_opts_arg);
    }

    return 0;
}

/**
 * \brief UART 设备写函数
 */
static int __dev_write(void      *p_dev,
                        uint8_t  *p_data,
                        uint32_t  data_len){
    return Wed_uart_write((struct Wed_uart *)p_dev, p_data, data_len);
}

/* \brief 设备操作函数集 */
static struct Wed_dev_opts __g_uart_opts = {
        .p_fn_init  = __dev_init,
        .p_fn_write = __dev_write,
};

/**
 * \brief UART 设备注册函数
 *
 * @param p_uart_name UART 名字
 * @param p_opts      UART 设备操作函数集
 * @param p_usr_data  操作函数参数
 * @param flag        标志
 *
 * @return 成功返回 0
 */
int Wed_uart_register(char                 *p_uart_name,
                      struct Wed_uart_opts *p_opts,
                      void                 *p_opts_arg,
                      uint32_t              flag){
    int                  ret;
    struct Wed_uart *p_uart_dev = NULL;

    if ((p_uart_name == NULL) ||
            (strlen(p_uart_name) >= WED_DEV_NAME_LEN) ||
            (p_opts == NULL)) {
        return -WED_EINVAL;
    }

    /* 检查设备是否已存在 */
    if (Wed_dev_find_by_name(p_uart_name)) {
        return -WED_EEXIST;
    }

#if WED_MEM_DYMANICS
    p_uart_dev = Wed_calloc(1, sizeof(struct Wed_uart));
    if (p_uart_dev == NULL) {
        return -WED_ENOMEM;
    }

    p_uart_dev->p_opts     = p_opts;
    p_uart_dev->p_opts_arg = p_opts_arg;
    p_uart_dev->flag       = flag;

    ret = Wed_mutex_init(&p_uart_dev->tx_lock);
    if (ret != 0) {
        goto __failed;
    }

    ret = Wed_mutex_init(&p_uart_dev->rx_lock);
    if (ret != 0) {
        goto __failed;
    }

    ret = Wed_dev_init_add(&p_uart_dev->dev,
                            p_uart_name,
                            WED_DEV_TYPE_UART,
                         &__g_uart_opts,
                            p_uart_dev);
    if (ret == 0) {
        __dev_add(p_uart_dev);

        return 0;
    }

__failed:
    Wed_free(p_uart_dev);

    return ret;
#else

#endif
}

/**
 * \brief UART 设备写函数
 *
 * @param p_uart_dev UART 设备结构体
 * @param p_data     要写的数据缓存
 * @param data_len   要写的数据长度
 *
 * @return 返回实际写的数据长度
 */
int Wed_uart_write(struct Wed_uart *p_uart_dev,
                   uint8_t         *p_data,
                   uint32_t         data_len){
    int      ret;
    uint32_t i;

    if ((p_uart_dev == NULL) ||
            (p_data == NULL) ||
            (data_len == 0)) {
        return -WED_EINVAL;
    }

    if (p_uart_dev->p_opts->p_fn_byte_write == NULL) {
        return -WED_ENOSUPPORT;
    }

    ret = Wed_mutex_lock(&p_uart_dev->tx_lock, 5000);
    if (ret != 0) {
        return ret;
    }

    if ((p_uart_dev->flag & WED_UART_TX_FLAG_MASK) == WED_UART_TX_POLL_FLAG) {
        for (i = 0; i < data_len; i++) {
            ret = p_uart_dev->p_opts->p_fn_byte_write(p_uart_dev->p_opts_arg, p_data[i]);
            if (ret != 1) {
                break;
            }
        }
    } else {
        //todo
    }

    Wed_mutex_unlock(&p_uart_dev->tx_lock);

    return i;
}

